package com.gjt.serverSocket;

import javax.net.ServerSocketFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.Arrays;

/**
 * Created with Intel IDEA.
 *
 * @Auther: gjt
 * @Date: 2024/9/21 11:29
 */
public class SecureOrderTaker {
    public final static int PORT = 7000;
    public final static String algorithm = "SSL";

    public static void main(String[] args) {
        try {
            // 获取SSLContext实例
            SSLContext sslContext = SSLContext.getInstance(algorithm);

            // 参考实现只支持x.509 的密钥
            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");

            // oracle 默认密钥数据库类型
            KeyStore ks = KeyStore.getInstance("JKS");

            /**
             * 每个密钥库都必须有口令短语加密，在磁盘加载前必须提供这个口令。口令短语以char[]数组形式存储，
             * 可以很快的从磁盘擦除，而不是等待回收
             */
            // 生成的口令
            char[] password = System.console().readPassword();

            // 加载口令和密钥文件到 密钥库
            ks.load(Files.newInputStream(Paths.get("jnp4e.keys")), password);
            // 初始化密钥管理器
            kmf.init(ks, password);
            // 初始化SSL上下文
            sslContext.init(kmf.getKeyManagers(), null, null);


            // 擦除口令
            Arrays.fill(password, '0');

            SSLServerSocketFactory serverSocketFactory = sslContext.getServerSocketFactory();

            SSLServerSocket server = (SSLServerSocket) serverSocketFactory.createServerSocket(PORT);

            // 增加匿名（未认证）密码组
            String[] supported = server.getSupportedCipherSuites(); // 获取支持的密码组
            String[] annoCipherSuites = new String[supported.length];
            int numAnonCipherSuites = 0;
            for (int i = 0; i < supported.length; i++) {
                if (supported[i].indexOf("_anon_") > 0) {
                    annoCipherSuites[numAnonCipherSuites++] = supported[i];
                }
            }


            // 获取启用的密码组
            String[] oldEnabled = server.getEnabledCipherSuites();
            String[] newEnabled = new String[numAnonCipherSuites + oldEnabled.length];

            /**
             * 参数介绍
             *  src: 原数组； position: 原数组的起始位置； dest: 目标数组； destPos: 目标数组的起始位置； length: 要复制的元素数量
             */
            System.arraycopy(oldEnabled, 0, newEnabled, 0, oldEnabled.length);
            System.arraycopy(annoCipherSuites, 0, newEnabled, oldEnabled.length, numAnonCipherSuites);

            server.setEnabledCipherSuites(newEnabled);

            /**
             * 服务端的会话管理
             *  setEnableSessionCreation(true); true: 表示可以建立会话； false: 表示不可以建立会话
             *  getEnableSessionCreation（）： 确定当前是否允许建立会话
             *  如果服务端禁止会话，客户端仍然可以连接，只不过它得不到一个会话，而必须为每个socket 再次完成握手。
             *  如果客户端禁止会话，服务端可以，他们仍然可以在没有会话的情况下进行对话
             */
            server.setEnableSessionCreation(true);
            server.getEnableSessionCreation();

            // 现在所有设置工作已经完成，可以进行实际通信
            while (true) {
                // 这个是socket 是安全的， 但是从代码中看不出任何痕迹
                try (Socket theConnection = server.accept()) {
                    InputStream inputStream = theConnection.getInputStream();
                    int c;
                    while ((c = inputStream.read()) != -1) {
                        System.out.write(c);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } catch (NoSuchAlgorithmException | KeyStoreException | CertificateException | IOException |
                 UnrecoverableKeyException | KeyManagementException e) {
            throw new RuntimeException(e);
        }
    }
}
